2 // @name Thread Rebuilder
3 // @namespace http://tampermonkey.net/
5 // @description try to take over the world!
6 // @author ECHibiki /qa/
7 // @match https://boards.4chan.org/*/thread/*
8 // @match http://boards.4chan.org/*/thread/*
9 // @grant GM_xmlhttpRequest
10 // @updateURL https://github.com/ECHibiki/4chan-UserScripts/raw/master/Thread-Rebuilder.user.js
11 // @downloadURL https://github.com/ECHibiki/4chan-UserScripts/raw/master/Thread-Rebuilder.user.js
12 // @run-at document-start
16 var thread_data = [['Comment'], ['Image URLs'], ['Image Names'] ,['Post No.']];
18 var semaphore_posts = 1;
21 var use_offsite_archive = false;
22 var window_displayed = false;
23 var in_sequence = false;
24 var tool_top_visible = false;
26 var help_icon_source = " data:image/jpeg;base64,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";
29 //set listener to build interface in 4chanX
30 //set listeners to build interface in 4chanX
31 document.addEventListener("4chanXInitFinished", function(e){
32 document.addEventListener("QRDialogCreation", enhance4ChanX);
37 use_offsite_archive = localStorage.getItem("ArchiveType") == 0 ? true : false;
38 if(use_offsite_archive) document.getElementById("OffsiteArchive").checked = true;
39 else document.getElementById("OnsiteArchive").checked = true;
45 function storageAvailable(type) {
47 var storage = window[type],
48 x = '__storage_test__';
49 storage.setItem(x, x);
50 storage.removeItem(x);
54 //From https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
55 return e instanceof DOMException && (
56 // everything except Firefox
60 // test name field too, because code might not be present
61 // everything except Firefox
62 e.name === 'QuotaExceededError' ||
64 e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
65 // acknowledge QuotaExceededError only if there's something already stored
70 //settings for time expiration on image hiding
71 function rebuildWindow(){
72 var style = document.createElement('style');
73 style.innerHTML = ".inputs{background-color:rgb(200,200,200);margin:5px 7px;width:100px;}";
74 document.body.appendChild(style);
76 var background_div = document.createElement("div");
77 background_div.setAttribute("style", "border:solid 1px black;position:fixed;width:100%;height:100%;background-color:rgba(200,200,200,0.3);top:0;left:0;display:none; z-index:9");
78 background_div.setAttribute("id", "rebuildBackground");
79 document.body.appendChild(background_div);
80 background_div.addEventListener("click", rebuildToggle);
82 var window_div = document.createElement("div");
83 window_div.setAttribute("style", "border:solid 1px black;position:fixed;width:400px;background-color:rgb(200,200,200);left:40%;top:20%;margin-bottom:0; display:none; z-index:10");
84 window_div.setAttribute("id", "rebuildWindow");
86 var close_div = document.createElement("div");
87 close_div.setAttribute("style", "border:solid 1px black;position:absolute;width:25px;height:25px;background-color:rgba(255,100,90,0.9); right:3px;top:3px; z-index:10");
88 close_div.addEventListener("click", rebuildToggle);
89 window_div.appendChild(close_div);
91 var title_para = document.createElement("p");
92 title_para.setAttribute("style", "margin-left:5px;margin-top:5px");
93 var title_text = document.createTextNode("Rebuild Settings");
94 title_para.appendChild(title_text);
95 window_div.appendChild(title_para);
97 var container_div = document.createElement("div");
98 container_div.setAttribute("style","background-color:white;margin:0 0;padding:5px;");
99 window_div.appendChild(container_div);
101 var rebuild_label_local = document.createElement("label");
102 var rebuild_text_local = document.createTextNode("Use 4chan Archives: ");
103 rebuild_label_local.appendChild(rebuild_text_local);
104 container_div.appendChild(rebuild_label_local);
105 var rebuild_input_local = document.createElement("input");
106 rebuild_input_local.setAttribute("type", "radio");
107 rebuild_input_local.setAttribute("name", "ArchiveSettings");
108 rebuild_input_local.setAttribute("id", "OnsiteArchive");
109 container_div.appendChild(rebuild_input_local);
110 container_div.appendChild(rebuild_input_local);
111 container_div.appendChild(document.createElement("br"));
113 var rebuild_label_offsite = document.createElement("label");
114 var rebuild_text_offsite = document.createTextNode("Use Offsite Archives: ");
115 rebuild_label_offsite.appendChild(rebuild_text_offsite);
116 container_div.appendChild(rebuild_label_offsite);
117 var rebuild_input_offsite = document.createElement("input");
118 rebuild_input_offsite.setAttribute("type", "radio");
119 rebuild_input_offsite.setAttribute("name", "ArchiveSettings");
120 rebuild_input_offsite.setAttribute("id", "OffsiteArchive");
121 container_div.appendChild(rebuild_input_offsite);
122 container_div.appendChild(rebuild_input_offsite);
123 container_div.appendChild(document.createElement("br"));
125 var set_button = document.createElement("input");
126 set_button.setAttribute("type", "button");
127 set_button.setAttribute("id", "setTime");
128 set_button.setAttribute("value", "Set Archive");
129 set_button.addEventListener("click", function(){
130 if (storageAvailable('localStorage')) {
131 var radio_options = document.getElementsByName("ArchiveSettings");
132 for (var radio_input = 0 ; radio_input < radio_options.length; radio_input++)
133 if(radio_options[radio_input].checked){
134 localStorage.setItem("ArchiveType", radio_input);
135 if(radio_input == 0) use_offsite_archive = true;
140 container_div.appendChild(set_button);
142 document.body.appendChild(window_div);
146 function rebuildToggle(){
147 if(window_displayed){
148 document.getElementById("rebuildWindow").style.display = "none";
149 document.getElementById("rebuildBackground").style.display = "none";
150 window_displayed = false;
153 document.getElementById("rebuildWindow").style.display = "inline-block";
154 document.getElementById("rebuildBackground").style.display = "inline-block";
155 window_displayed = true;
159 function rebuildButton(){
160 var rebuild_button = document.createElement("input");
161 rebuild_button.setAttribute("Value", "Thread Rebuilder Settings");
162 rebuild_button.setAttribute("type", "button");
163 rebuild_button.setAttribute("style", "position:absolute;top:105px");
164 rebuild_button.addEventListener("click", rebuildWindow);
165 if(document.body === null){
166 setTimeout(rebuildButton, 30);
169 document.body.appendChild(rebuild_button);
170 rebuild_button.addEventListener("click", rebuildToggle);
174 var enhance4ChanX = function(){
175 var qr_window = document.getElementById("qr");
177 if(document.getElementById("qrRebuilder") !== null) qr_window.removeChild(document.getElementById("qrRebuilder"));
179 var thread_rebuilder_table = document.createElement("TABLE");
180 thread_rebuilder_table.setAttribute("id", "qrRebuilder");
181 thread_rebuilder_table.setAttribute("style", "text-align:center");
182 qr_window.appendChild(thread_rebuilder_table);
184 var thread_row = document.createElement("TR");
186 var help_icon_container = document.createElement("A");
187 help_icon_container.href = "javascript:void(0)";
188 help_icon_container.title = "Click to View Help!";
189 var help_icon = document.createElement("IMG");
190 help_icon.setAttribute("style", "height:" + option_text_size * 1.25 + "px;margin:-4px 10px");
191 help_icon.src = help_icon_source;
193 help_icon_container.appendChild(help_icon);
194 thread_row.appendChild(help_icon_container);
196 var tooltip_div = document.createElement("DIV");
197 tooltip_div.innerHTML = "Insert the thread number of the post to rebuild<br/>Must be in either the 4chan archives or archived.moe<hr/>Submit bugs to <a href='https://github.com/ECHibiki/4chan-UserScripts'>my Github</a>";
198 tooltip_div.setAttribute("style", "z-index:9;padding:5px;border:1px solid black;background-color:white;word-wrap:break-word;display:none;position:absolute;");
199 help_icon_container.addEventListener("click", function(ev){
201 tooltip_div.setAttribute("style", "z-index:9;padding:5px;border:1px solid black;background-color:white;word-wrap:break-word;display:none;position:absolute;");
203 tooltip_div.setAttribute("style", "z-index:9;padding:5px;border:1px solid black;background-color:white;word-wrap:break-word;display:block;position:absolute;"
204 + "left:" + (ev.clientX - qr_window.getBoundingClientRect().x) +
205 "px;top:" + (ev.clientY - qr_window.getBoundingClientRect().y ) + "px;");
206 tool_top_visible = !tool_top_visible;
208 qr_window.appendChild(tooltip_div);
210 var second_row_nodes = [
211 document.createTextNode("Thread: "),
212 document.createElement("INPUT"),
213 document.createElement("INPUT"),
215 second_row_nodes.forEach(
217 thread_row.appendChild(node);
219 thread_rebuilder_table.appendChild(thread_row);
221 second_row_nodes[1].setAttribute("ID", "threadInput");
222 second_row_nodes[1].setAttribute("style", "width:35.0%");
224 second_row_nodes[2].setAttribute("ID", "threadButton");
225 second_row_nodes[2].setAttribute("type", "button");
226 second_row_nodes[2].setAttribute("value", "Set Rebuild Queue");
228 second_row_nodes[2].addEventListener("click", function(){
231 getThread(second_row_nodes[1].value);
232 postID = setInterval(postRoutine, 1000);
233 if(timeListen === undefined) timeListen = setInterval(timeListenerFunction, 1000);
235 qr_window.appendChild(document.createElement("hr"));
238 var thread_data_length = 0;
239 var posts_created = 0;
241 var postRoutine = function(){
244 thread_data_length = thread_data[0].length;
245 fillID = setInterval(fillRoutine, 10);
250 var stopRoutine = function(){
251 clearInterval(postID);
255 var fillRoutine = function(){
256 if(posts_created >= thread_data_length) {semaphore_posts = 0 ; stopFillRoutine();}
257 else if(semaphore_posts == 1){
259 createPost(thread_data[0][posts_created], thread_data[1][posts_created], thread_data[2][posts_created]);
264 var stopFillRoutine = function(){
265 clearInterval(fillID);
268 var setPropperLinking = function(text){
269 var search_regex = RegExp(">>\\d+", "g");
272 var link_arr = Array();
273 while((result = search_regex.exec(text)) != null){
274 var end_index = search_regex.lastIndex;
275 var post_no = result.toString().replace(/>/g, "");
276 link_arr.push([post_no, end_index]);
278 //hunt down the text of what it linked to
279 //Get the links inside of the origonal message to show text contents
280 var responding_text = Array();
281 if(use_offsite_archive)
282 URL = "https://www.archived.moe/_/api/chan/thread/?board=" + board + "&num=" + document.getElementById("threadInput").value;
284 URL = "https://a.4cdn.org/" + board + "/thread/" + document.getElementById("threadInput").value + ".json";
285 var xhr = new GM_xmlhttpRequest(({
288 responseType : "json",
289 onload: function(data){
290 if(use_offsite_archive)
291 data = data.response["" + document.getElementById("threadInput").value]["posts"];
293 data = data.response["posts"];
294 if(data == undefined){
295 alert("Invalid Thread ID: " + document.getElementById("threadInput").value + ". ");
298 link_arr.forEach(function(link_item){
299 for(var data_entry = 0 ; data_entry < data.length ; data_entry++){
300 if(parseInt(link_item[0]) == parseInt(data[data_entry]["no"])){
301 if(use_offsite_archive)
302 responding_text.push([ [post_no, end_index], data[data_entry]["comment_processed"].replace(/(>>|https:\/\/www\.archived\.moe\/.*\/thread\/.*\/#)\d+/g, ""), link_item["media"]["safe_media_hash"] ]);
304 responding_text.push([ [post_no, end_index], data[data_entry]["com"].replace(/(>>|#p)\d+/g, ""), data[data_entry]["md5"] ]);
310 var current_url = window.location.href;
311 var hash_index = current_url.lastIndexOf("#") != -1 ? current_url.lastIndexOf("#"): window.location.href.length;
312 var current_thread = window.location.href.substring(current_url.lastIndexOf("/")+1, hash_index);
313 var current_url = "https://a.4cdn.org/" + board + "/thread/" + current_thread + ".json";
314 //open current thread to hunt down the text found in links
315 var xhr = new GM_xmlhttpRequest(({
318 responseType : "json",
319 onload: function(data){
320 data = data.response["posts"];
321 if(data == undefined){
322 alert("Invalid Thread ID: " + document.getElementById("threadInput").value + ". ");
325 responding_text.forEach(function(response_item){
326 for(var data_entry = 0 ; data_entry < data.length ; data_entry++){
327 if((response_item[1] == data[data_entry]["com"].replace(/(>>|#p)\d+/g, "") || response_item[1] == null)
328 && (response_item[2] == data[data_entry]["md5"] || response_item[2] == null)){
329 var start_index = response_item[0][0].legth - response_item[0][1];
330 text = text.substring(0, start_index) + ">>" + data[data_entry]["no"] + text.substring(response_item[0][1]);
335 document.getElementById("qr").getElementsByTagName("TEXTAREA")[0].value = text;
336 document.getElementById("add-post").click();
347 //2) GET ARCHIVED THREAD
348 var getThread = function(threadNo){
349 thread_data = [[], [], [], []];
351 if(use_offsite_archive)
352 URL = "https://www.archived.moe/_/api/chan/thread/?board=" + board + "&num=" + document.getElementById("threadInput").value;
354 URL = "https://a.4cdn.org/" + board + "/thread/" + document.getElementById("threadInput").value + ".json";
355 var xhr = new GM_xmlhttpRequest(({
358 responseType : "json",
359 onload: function(data){
360 var starting_post = -1;
361 if(use_offsite_archive){
363 data = data.response["" + document.getElementById("threadInput").value];
367 data = data.response;
369 if(data == undefined){
370 alert("Invalid Thread ID: " + threadNo + ".\n4chan Archive ");
373 if(use_offsite_archive) data["posts"] = Object.values(data["posts"]);
374 var len = data["posts"].length;
376 for(var post_number = starting_post ; post_number < len ; post_number++){
377 var comment = undefined;
378 if(use_offsite_archive)
379 comment = data["posts"][post_number]["comment"];
381 comment = data["posts"][post_number]["com"];
382 if(comment !== undefined && comment !== null)
383 thread_data[0].push(comment);
385 thread_data[0].push(-1);
387 var filename = undefined;
388 if(use_offsite_archive)
389 if(data["posts"][post_number]["media"] !== null)
390 filename = "" + data["posts"][post_number]["media"]["media_filename"];
392 filename = "" + data["posts"][post_number]["tim"] + data["posts"][post_number]["ext"];
394 if(filename !== undefined && filename !== null && filename.indexOf("undefined") == -1)
395 if(use_offsite_archive)
396 if(data["posts"][post_number]["media"] !== null)
397 thread_data[1].push(data["posts"][post_number]["media"]["remote_media_link"]);
398 else thread_data[1].push(-1);
400 thread_data[1].push("https://i.4cdn.org/" + board + "/" + filename);
401 else thread_data[1].push(-1);
403 if(use_offsite_archive)
404 if(data["posts"][post_number]["media"] !== null)
405 thread_data[2].push(data["posts"][post_number]["media"]["media_id"]);
407 thread_data[2].push(data["posts"][post_number]["filename"]);
409 if(use_offsite_archive)
410 thread_data[3].push(data["posts"][post_number]["num"]);
412 thread_data[3].push(data["posts"][post_number]["no"]);
419 //3) RIP POSTS AND IMAGES
420 var createPost = function(text, imageURL, imageName){
422 var response_type = "arraybuffer";
423 if(use_offsite_archive) response_type = "text"
424 var xhr = new GM_xmlhttpRequest(({
427 responseType : response_type,
428 onload: function(response)
430 if(use_offsite_archive){
431 var parser = new DOMParser();
432 var content_attribute = parser.parseFromString(response.response, "text/html").getElementsByTagName("META")[0].getAttribute("content");
433 var redirect_url = content_attribute.substring(content_attribute.indexOf("http"));
434 var xhr = new GM_xmlhttpRequest(({method:"GET", url: redirect_url, responseType:"arraybuffer",
435 onload:function(response){
436 inputImage(response, text, imageURL, imageName);
441 inputImage(response, text, imageURL, imageName);
447 text = createPostComment(text);
448 setPropperLinking(text);
452 function inputImage(response, text, imageURL, imageName){
455 if(imageURL.indexOf(".jpg") > -1){
456 blob = new Blob([response.response], {type:"image/jpeg"});
459 else if(imageURL.indexOf(".png") > -1){
460 blob = new Blob([response.response], {type:"image/png"});
463 else if(imageURL.indexOf(".gif") > -1){
464 blob = new Blob([response.response], {type:"image/gif"});
467 else if(imageURL.indexOf(".webm") > -1){
468 blob = new Blob([response.response], {type:"video/webm"});
472 var name = imageName + ext;
474 //SEND RESULTING RESPONSE TO 4CHANX FILES === QRSetFile
475 var detail = {file:blob, name:name};
476 if (typeof cloneInto === 'function') {
477 detail = cloneInto(detail , document.defaultView);
480 document.dispatchEvent(new CustomEvent('QRSetFile', {bubbles:true, detail}));
482 if(text !== "" && text !== undefined && text !== -1) {
483 text = createPostComment(text);
484 setPropperLinking(text);
487 document.getElementById("add-post").click();
492 //4) CREATE POST QUEUE
493 var createPostComment = function(text){
494 text = text.replace(/<a href="\/[a-zA-Z]+\/" class="quotelink">/g, "");
495 text = text.replace(/<span class="deadlink">/g, "");
497 var quote_regex = /<a href="#p[0-9]+" class="quotelink">>>[0-9]+/g;
498 var find = text.match(quote_regex);
500 find.forEach(function(match){
501 var index_start = text.indexOf(match);
502 var match_len = match.length;
503 var index_len = index_start + match_len;
504 var first_quote = match.indexOf('"');
505 var second_quote = match.indexOf('"', first_quote + 1);
506 var post_no = match.substring(first_quote + 3, second_quote);
508 match = ">>" + post_no;
510 text = text.substr(0, index_start) + match + text.substr(index_len);
514 text = text.replace(/<span class="quote">/g, "");
515 text = text.replace(/<br>/g, "\n");
516 text = text.replace(/'/g, "'");
517 text = text.replace(/>/g, ">");
518 text = text.replace(/<\/a>/g, "");
519 text = text.replace(/<wbr>/g, "");
520 text = text.replace(/<\/span>/g, "");
526 var timeListenerFunction = function(){
527 var time = document.getElementById("qr-filename-container").nextSibling.value.replace(/[a-zA-Z]+/g, "");
536 document.addEventListener('QRPostSuccessful', function(e) {
538 document.getElementById("dump-list").childNodes[1].click();
539 setPropperLinking(document.getElementById("qr").getElementsByTagName("TEXTAREA")[0].value);
545 thread_data_length = 0;
553 thread_data = [['Comment'], ['Image URLs'], ['Image Names'] ,['Post No.']];
555 var qr_dumplist = document.getElementById("dump-list").childNodes;
556 var qr_dumplist_len = qr_dumplist.length;
557 var current_preview = 0;
558 while(qr_dumplist_len - current_preview > 1){
559 qr_dumplist[0].firstChild.click();